using System.Linq.Expressions;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Authentication;
using Core;
using Microsoft.Extensions.Options;
using SuperSocket.Client;

namespace Client;

public sealed class CommandHandle
{
    private readonly MethodInfo _method;
    private readonly bool _isTaskGenericType;
    private readonly object? _delegateTarget;
    private readonly Func<object?, object[], Task> _handler;
    private readonly IEasyClient<BaseCommand, BaseCommand> _client;

    public CommandHandle(MethodInfo method, IEasyClient<BaseCommand, BaseCommand> client, object? delegateTarget = null)
    {
        //获取该方法返回类型是否为 Task<T> 
        var isTaskGenericType = method.ReturnType.IsGenericType &&
                                method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);

        _method = method;
        _client = client;
        _delegateTarget = delegateTarget;
        _handler = CreateInvoker(method);
        _isTaskGenericType = isTaskGenericType;
    }

    public async Task InvokeAsync(BaseCommand command)
    {
        Task resultTask;

        try
        {
            resultTask = _handler.Invoke(_delegateTarget, new[] { command });
        }
        catch (Exception ex)
        {
            throw new Exception($"执行{_method.Name}抛出一个异常", ex);
        }

        //如果该方法返回的类型是 task<> 范型类型 则需要回复服务器信息
        if (_isTaskGenericType)
        {
            var result = await resultTask.ToTask<BaseCommand>();

            await _client.SendAsync(result);
        }
        else
        {
            await resultTask;
        }
    }

    private Func<object?, object[], Task> CreateInvoker(MethodInfo methodInfo)
    {
        const string instanceName = "instance";
        const string parametersName = "parameters";

        var instance = Expression.Parameter(typeof(object), instanceName);

        var parameters = Expression.Parameter(typeof(object[]), parametersName);

        var instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instance, methodInfo.DeclaringType!);

        var parametersCast = methodInfo.GetParameters().Select((p, i) =>
        {
            var parameter = Expression.ArrayIndex(parameters, Expression.Constant(i));
            return Expression.Convert(parameter, p.ParameterType);
        });

        var body = Expression.Call(instanceCast, methodInfo, parametersCast);

        var bodyCast = Expression.Convert(body, typeof(Task));

        return Expression.Lambda<Func<object?, object[], Task>>(bodyCast, instance, parameters).Compile();
    }
}

public sealed class PlayClient
{
    private Timer _heartTimer;
    private readonly PlayPacketEncode _packetEncode = new();
    private readonly PacketDispatcher _packetDispatcher = new();
    private readonly PacketIdentifierProvider _packetIdentifierProvider = new();

    private readonly IEasyClient<BaseCommand, BaseCommand> _client =
        new EasyClient<BaseCommand, BaseCommand>(new PlayPipeLineFilter(), new PlayPacketEncode());

    private readonly Dictionary<BaseCommand.Types.CommandType, CommandHandle> _handlers = new(20);

    public PlayClient()
    {
        _client.Closed += OnClosed;
        _client.PackageHandler += OnPackageHandler;
        //_client.Security = GetSecurityOptions();
        _heartTimer = new Timer(OnHeartBeat, null, Timeout.Infinite, Timeout.Infinite);
    }

    public async ValueTask StartAsync()
    {
        Console.WriteLine("连接成功");

        var connected = await _client.ConnectAsync(new DnsEndPoint("127.0.0.1", 4040, AddressFamily.InterNetwork));

        Console.WriteLine($"连接结果: {connected}");

        _client.StartReceive();
    }

    public async ValueTask<BaseCommand> LoginAsync(string username, string password)
    {
        var command = new BaseCommand
        {
            Key = BaseCommand.Types.CommandType.Login,
            Login = new CommandLogin
            {
                Username = username,
                Password = password,
            }
        };

        var reply = await SendAndReceivePacketAsync(command, CancellationToken.None);

        if (reply.SuccessFul)
            OnHeartBeat(null);

        return reply;
    }

    public PlayClient RegisterReplyHandle(BaseCommand.Types.CommandType commandType,
        Func<BaseCommand, Task<BaseCommand>> handler)
    {
        var commandHandle = new CommandHandle(handler.Method, _client, handler.Target);

        if (!_handlers.TryAdd(commandType, commandHandle))
            throw new Exception($"{commandType}该命令已经注册");

        return this;
    }

    public PlayClient RegisterHandle(BaseCommand.Types.CommandType commandType, Func<BaseCommand, Task> handler)
    {
        var commandHandle = new CommandHandle(handler.Method, _client, handler.Target);

        if (!_handlers.TryAdd(commandType, commandHandle))
            throw new Exception($"{commandType}该命令已经注册");

        return this;
    }

    private SecurityOptions GetSecurityOptions()
    {
        return new SecurityOptions
        {
            TargetHost = "supersocket",
            EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
            RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
        };
    }

    private async ValueTask<BaseCommand> SendAndReceivePacketAsync(
        BaseCommand command,
        CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();

        command.RequestId = _packetIdentifierProvider.GetNextRequestId();

        using var packetAwaitable = _packetDispatcher.AddAwaitable(command.RequestId);

        try
        {
            await _client.SendAsync(command);
        }
        catch (Exception e)
        {
            packetAwaitable.Fail(e);
            Console.Error.WriteLine($"发送封包抛出一个异常 key：{command.Key}");
        }

        try
        {
            return await packetAwaitable.WaitAsync(cancellationToken);
        }
        catch (Exception e)
        {
            if (e is TimeoutException)
                Console.Error.WriteLine($"等待封包调度超时 key：{command.Key}");

            throw;
        }
    }

    async void OnHeartBeat(object? state)
    {
        var timer = _heartTimer;

        try
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);

            await _client.SendAsync(new BaseCommand
            {
                Key = BaseCommand.Types.CommandType.HeartBeat,
                HeartBeat = new CommandHeartBeat
                {
                    ClientTimesteamp = 1,
                }
            });
        }
        finally
        {
            timer.Change(5 * 1000, 5 * 1000);
        }
    }

    void OnClosed(object? sender, EventArgs e)
    {
        Console.WriteLine("断开连接");
    }

    async ValueTask OnPackageHandler(IEasyClient<BaseCommand>? client, BaseCommand packet)
    {
        await ValueTask.CompletedTask;

        Console.WriteLine($"key：{packet.Key}");

        switch (packet.Key)
        {
            case BaseCommand.Types.CommandType.LoginReply: //设置调度器
            case BaseCommand.Types.CommandType.RegisterReply: //设置调度器
                _packetDispatcher.TryDispatch(packet);
                return;
            case BaseCommand.Types.CommandType.HeartBeatReply: //无需处理
                return;
            default:
                break;
        }

        if (!_handlers.TryGetValue(packet.Key, out var commandHandle))
            return;

        ThreadPool.QueueUserWorkItem(async callBack => { await commandHandle.InvokeAsync(packet); });
    }
}